#include <vector>

#include "../../include/helpers/exception_handler.h"
#include "../../include/helpers/factories.h"
#include "../../include/helpers/resource.h"
#include "../../include/helpers/thread_state_pls.h"
#include "../../include/helpers/worker_ref.h"

#include "helpers/canvas_render_objects.h"
#include "helpers/image_decode.h"
#include "rive/math/math_types.hpp"
#include "rive/renderer/gl/render_buffer_gl_impl.hpp"
#include "rive/renderer/gl/render_context_gl_impl.hpp"
#include "rive/renderer/rive_render_factory.hpp"
#include "rive/renderer/rive_render_image.hpp"

using namespace rive;
using namespace rive::gpu;

namespace ohos_rive {
template <>
rive::rcp<rive::RenderImage> decode(rive::Span<const uint8_t> data, RendererType rendererType)
{
    rive::Factory *factory = GetFactory(rendererType);
    return factory->decodeImage(data);
}

template <>
rive::rcp<rive::Font> decode(rive::Span<const uint8_t> data, RendererType rendererType)
{
    rive::Factory *factory = GetFactory(rendererType);
    return factory->decodeFont(data);
}

template <>
rive::rcp<rive::AudioSource> decode(rive::Span<const uint8_t> data, RendererType rendererType)
{
    rive::Factory *factory = GetFactory(rendererType);
    return factory->decodeAudio(data);
}

// 自定义的RenderBuffer实现，不依赖RenderBufferGLImpl的具体实现
class PLSRenderBuffer : public rive::RiveRenderBuffer {
public:
    PLSRenderBuffer(rive::RenderBufferType type, rive::RenderBufferFlags flags, size_t sizeInBytes)
        : rive::RiveRenderBuffer(type, flags, sizeInBytes),
          m_glWorker(ohos_rive::RefWorker::RiveWorker()),
          m_bufferID(0),
          m_isInitialized(false),
          m_mappedData(nullptr),
          m_offThreadBufferDataMirror(nullptr)
    {
        if (std::this_thread::get_id() != m_glWorker->threadID()) {
            // 在非GL线程上创建，需要在GL线程上初始化
            rcp<PLSRenderBuffer> thisRef = ref_rcp(this);
            m_bufferCreationWorkID = m_glWorker->run([thisRef](ohos_rive::DrawableThreadState *threadState) {
                auto plsState = reinterpret_cast<ohos_rive::PLSThreadState *>(threadState);
                auto *renderContextImpl = plsState->renderContext()->static_impl_cast<rive::gpu::RenderContextGLImpl>();
                thisRef->initializeGL(renderContextImpl);
            });
        } else {
            // 在GL线程上直接初始化
            auto plsState = reinterpret_cast<ohos_rive::PLSThreadState *>(m_glWorker->threadState());
            auto *renderContextImpl = plsState->renderContext()->static_impl_cast<rive::gpu::RenderContextGLImpl>();
            initializeGL(renderContextImpl);
            m_bufferCreationWorkID = ohos_rive::WorkerThread::kWorkIDAlwaysFinished;
        }
    }

    ~PLSRenderBuffer() override
    {
        if (!m_isInitialized)
            return;

        if (std::this_thread::get_id() != m_glWorker->threadID()) {
            m_glWorker->waitUntilComplete(m_bufferCreationWorkID);

            GLuint bufferToDelete = m_bufferID;
            if (bufferToDelete != 0) {
                m_glWorker->run(
                    [bufferToDelete](ohos_rive::DrawableThreadState *) { glDeleteBuffers(1, &bufferToDelete); });
            }
        } else if (m_bufferID != 0) {
            glDeleteBuffers(1, &m_bufferID);
        }
    }

    void *onMap() override
    {
        if (std::this_thread::get_id() != m_glWorker->threadID()) {
            if (!m_offThreadBufferDataMirror) {
                m_offThreadBufferDataMirror.reset(new uint8_t[sizeInBytes()]);
            }
            return m_offThreadBufferDataMirror.get();
        } else {
            ensureInitialized();

            if (!m_mappedData) {
                // 分配映射内存
                m_mappedData.reset(new uint8_t[sizeInBytes()]);
            }
            return m_mappedData.get();
        }
    }

    void onUnmap() override
    {
        if (std::this_thread::get_id() != m_glWorker->threadID() && m_offThreadBufferDataMirror) {
            const uint8_t *sideBufferData = m_offThreadBufferDataMirror.release();
            rcp<PLSRenderBuffer> thisRef = ref_rcp(this);

            m_glWorker->run([sideBufferData, thisRef](ohos_rive::DrawableThreadState *) {
                thisRef->ensureInitialized();

                if (thisRef->m_bufferID == 0) {
                    glGenBuffers(1, &thisRef->m_bufferID);
                }

                glBindBuffer(GL_ARRAY_BUFFER, thisRef->m_bufferID);
                glBufferData(GL_ARRAY_BUFFER, thisRef->sizeInBytes(), sideBufferData, GL_DYNAMIC_DRAW);
                glBindBuffer(GL_ARRAY_BUFFER, 0);

                delete[] sideBufferData;
            });
        } else if (std::this_thread::get_id() == m_glWorker->threadID() && m_mappedData) {
            ensureInitialized();

            if (m_bufferID == 0) {
                glGenBuffers(1, &m_bufferID);
            }

            glBindBuffer(GL_ARRAY_BUFFER, m_bufferID);
            glBufferData(GL_ARRAY_BUFFER, sizeInBytes(), m_mappedData.get(), GL_DYNAMIC_DRAW);
            glBindBuffer(GL_ARRAY_BUFFER, 0);
        }
    }

    GLuint bufferID() const
    {
        return m_bufferID;
    }

    void ensureInitialized()
    {
        if (!m_isInitialized && m_glWorker->threadID() == std::this_thread::get_id()) {
            // 如果在GL线程上但未初始化，尝试初始化
            auto plsState = reinterpret_cast<ohos_rive::PLSThreadState *>(m_glWorker->threadState());
            if (plsState) {
                auto *renderContextImpl = plsState->renderContext()->static_impl_cast<rive::gpu::RenderContextGLImpl>();
                initializeGL(renderContextImpl);
            }
        }
    }

private:
    void initializeGL(rive::gpu::RenderContextGLImpl *contextImpl)
    {
        if (m_isInitialized || contextImpl == nullptr)
            return;

        glGenBuffers(1, &m_bufferID);
        glBindBuffer(GL_ARRAY_BUFFER, m_bufferID);
        glBufferData(GL_ARRAY_BUFFER, sizeInBytes(), nullptr, GL_DYNAMIC_DRAW);
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        m_isInitialized = true;
    }

    const rcp<ohos_rive::RefWorker> m_glWorker;
    GLuint m_bufferID;
    bool m_isInitialized;
    std::unique_ptr<uint8_t[]> m_mappedData;
    std::unique_ptr<uint8_t[]> m_offThreadBufferDataMirror;
    ohos_rive::RefWorker::WorkID m_bufferCreationWorkID;
};

OhosImage::OhosImage(int width, int height, std::unique_ptr<const uint8_t[]> imageDataRGBAPtr)
    : RiveRenderImage(width, height), m_glWorker(ohos_rive::RefWorker::RiveWorker())
{
    // Create the texture on the worker thread where the GL context is
    // current.
    const uint8_t *imageDataRGBA = imageDataRGBAPtr.release();
    m_textureCreationWorkID = m_glWorker->run([this, imageDataRGBA](ohos_rive::DrawableThreadState *threadState) {
        auto plsState = reinterpret_cast<ohos_rive::PLSThreadState *>(threadState);
        uint32_t mipLevelCount = math::msb(m_Height | m_Width);
        auto *renderContextImpl = plsState->renderContext()->static_impl_cast<RenderContextGLImpl>();
        resetTexture(renderContextImpl->makeImageTexture(m_Width, m_Height, mipLevelCount, imageDataRGBA));
        delete[] imageDataRGBA;
    });
}

OhosImage::~OhosImage()
{
    // Ensure we are done initializing the texture before we turn around and
    // delete it.
    m_glWorker->waitUntilComplete(m_textureCreationWorkID);
    // Since this is the destructor, we know nobody else is using this
    // object anymore and there is not a race condition from accessing the
    // texture from any thread.
    Texture *texture = releaseTexture();
    if (texture != nullptr) {
        // Delete the texture on the worker thread where the GL context is
        // current.
        m_glWorker->run([texture](ohos_rive::DrawableThreadState *) { texture->unref(); });
    }
}

rcp<RenderBuffer> OhosRiveRenderFactory::makeRenderBuffer(RenderBufferType type,
                                                          RenderBufferFlags flags,
                                                          size_t sizeInBytes)
{
    return make_rcp<PLSRenderBuffer>(type, flags, sizeInBytes);
}

rcp<RenderImage> OhosRiveRenderFactory::decodeImage(Span<const uint8_t> encodedBytes)
{
    return renderImageFromNapiDecode(this->env, encodedBytes, false);
}

/** OhosCanvasFactory */
rive::rcp<rive::RenderBuffer> OhosCanvasFactory::makeRenderBuffer(rive::RenderBufferType type,
                                                                  rive::RenderBufferFlags flags,
                                                                  size_t sizeInBytes)
{
    return rive::make_rcp<rive::DataRenderBuffer>(type, flags, sizeInBytes);
}

rive::rcp<rive::RenderImage> OhosCanvasFactory::decodeImage(rive::Span<const uint8_t> encodedBytes)
{
    return make_rcp<CanvasRenderImage>(encodedBytes);
}

rive::rcp<rive::RenderShader> OhosCanvasFactory::makeLinearGradient(float sx,
                                                                    float sy,
                                                                    float ex,
                                                                    float ey,
                                                                    const rive::ColorInt colors[],
                                                                    const float stops[],
                                                                    size_t count)
{
    return rive::rcp<rive::RenderShader>(new LinearGradientCanvasShader(sx, sy, ex, ey, colors, stops, count));
}

rive::rcp<rive::RenderShader> OhosCanvasFactory::makeRadialGradient(float cx,
                                                                    float cy,
                                                                    float radius,
                                                                    const rive::ColorInt colors[],
                                                                    const float stops[],
                                                                    size_t count)
{
    return rive::rcp<rive::RenderShader>(new RadialGradientCanvasShader(cx, cy, radius, colors, stops, count));
}

rive::rcp<rive::RenderPath> OhosCanvasFactory::makeRenderPath(rive::RawPath &rawPath, rive::FillRule fillRule)
{
    return rive::make_rcp<CanvasRenderPath>(rawPath, fillRule);
}

rive::rcp<rive::RenderPath> OhosCanvasFactory::makeEmptyRenderPath()
{
    return rive::make_rcp<CanvasRenderPath>();
}

rive::rcp<rive::RenderPaint> OhosCanvasFactory::makeRenderPaint()
{
    return rive::make_rcp<CanvasRenderPaint>();
}
} // namespace ohos_rive